package com.quectel.util.common;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.quectel.util.ex.CheckErrorException;
import com.quectel.util.ex.common.DateParseException;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.Setter;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import reactor.util.function.Tuple2;
import reactor.util.function.Tuples;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;

public class DateUtils {

    @AllArgsConstructor
    public enum FormatType {
        MONTH("yyyy-MM"),
        SIMPLE("yyyy-MM-dd"),
        SIMPLE_WITHOUT_SEPARATOR("yyyyMMdd"),
        COMMON("yyyy-MM-dd HH:mm:ss"),
        BCD("yyMMddHHmmss"),
        MILLI("yyyy/MM/dd HH:mm:ss.SSS"),
        TIME("HH:mm:ss"),
        TIME_WITHOUT_SECONDS("HH:mm"),
        DAY("MM-dd"),
        TZ("yyyyMMdd'T'HHmmss'Z'"),
        NORM_DATETIME_MINUTE_FORMAT("yyyy-MM-dd HH:mm");
        @Setter
        @Getter
        private String type;
    }

    /**
     * 时间长度转为  几分 几秒 ....
     */
    @AllArgsConstructor
    public enum ConvertType {
        /**
         * 转为秒
         */
        s(1),
        /**
         * 转为分秒
         */
        m_s(2),
        /**
         * 转为时分秒
         */
        h_m_s(3),
        /**
         * 转为天时分秒
         */
        d_h_m_s(4);

        @Setter
        @Getter
        private int type;
    }

    /**
     * 得到当前时间
     *
     * @param formatType
     * @return
     */
    public static String getCurrentDateStr(FormatType formatType) {
        if (formatType == null) {
            formatType = FormatType.SIMPLE;
        }
        return format(new Date(), formatType);
    }

    /**
     * str转为date
     *
     * @param dateStr
     * @param formatType
     * @return
     */
    public static Date parse(String dateStr, FormatType formatType) {
        if (StringUtils.isBlank(dateStr)) {
            return null;
        }
        if (formatType == null) {
            formatType = FormatType.SIMPLE;
        }
        try {
            return new SimpleDateFormat(formatType.getType()).parse(dateStr);
        } catch (ParseException e) {
            throw new DateParseException(e);
        }
    }

    /**
     * 日期转为str
     *
     * @param date
     * @param formatType
     * @return
     */
    public static String format(Date date, FormatType formatType) {
        if (date == null) {
            return null;
        }
        if (formatType == null) {
            formatType = FormatType.SIMPLE;
        }
        return new SimpleDateFormat(formatType.getType()).format(date);
    }

    /**
     * 得到一个连续的时间段 2020-02-01 .... 2020-02-07
     *
     * @param startDayPos -6
     * @param endDayPos   0
     * @param isDesc      是否降序 默认升序
     * @return
     */
    public static String[] getPeriodDate(int startDayPos, int endDayPos, boolean isDesc) {
        if (endDayPos < startDayPos) {
            throw new CheckErrorException("结束位置一定要比开始位置大");
        }
        String[] result = new String[endDayPos - startDayPos + 1];

        long l = System.currentTimeMillis();
        for (int i = startDayPos, j = 0; i <= endDayPos; i++, j++) {
            result[j] = format(new Date(l + (long) i * 24 * 60 * 60 * 1000), FormatType.SIMPLE);
        }
        if (isDesc) {
            ArrayUtils.reverse(result);
        }
        return result;
    }

    /**
     * 转换类型不一样数组长度不一样
     *
     * @param durationMillis 单位毫秒
     * @param convertType
     * @return
     */
    public static long[] convertStr(long durationMillis, ConvertType convertType) {
        if (convertType.getType() == ConvertType.s.getType()) {
            //秒
            long s = durationMillis / 1000;
            return new long[]{s};
        } else if (convertType.getType() == ConvertType.m_s.getType()) {
            //分 秒
            long s = (durationMillis / 1000) % 60;
            long m = durationMillis / (60 * 1000);
            return new long[]{m, s};
        } else if (convertType.getType() == ConvertType.h_m_s.getType()) {
            //时 分 秒
            long s = (durationMillis / 1000) % 60;
            long m = (durationMillis / (60 * 1000)) % 60;
            long h = durationMillis / (60 * 60 * 1000);
            return new long[]{h, m, s};
        } else if (convertType.getType() == ConvertType.d_h_m_s.getType()) {
            //天 时 分 秒
            long s = (durationMillis / 1000) % 60;
            long m = (durationMillis / (60 * 1000)) % 60;
            long h = durationMillis / (60 * 60 * 1000) % 24;
            long d = durationMillis / (24 * 60 * 60 * 1000);
            return new long[]{d, h, m, s};
        }
        return null;
    }

    /**
     * 得到星期一到星期日的数字
     *
     * @param date
     * @return 1-7
     */
    public static int getWeekNum(Date date) {
        // 获得一个日历
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 指示一个星期中的某天。
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }
        return w == 0 ? 7 : w;
    }

    /**
     * 日期转星期
     *
     * @param date
     * @return
     */
    public static String dateToWeek(Date date) {
        // 获得一个日历
        Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        // 指示一个星期中的某天。
        int w = cal.get(Calendar.DAY_OF_WEEK) - 1;
        if (w < 0) {
            w = 0;
        }
        switch (w) {
            case 0:
                return "星期天";
            case 1:
                return "星期一";
            case 2:
                return "星期二";
            case 3:
                return "星期三";
            case 4:
                return "星期四";
            case 5:
                return "星期五";
            case 6:
                return "星期六";
            default:
                return "";
        }
    }

    /**
     * 按小时分段
     *
     * @param startTime 开始时间
     * @param endTime   结束时间
     * @param offset
     * @return
     */
    public static String[] timeSlotOfHour(Date startTime, Date endTime, FormatType format, int offset) {

        endTime = DateUtil.beginOfHour(endTime);

        Date realEndTime = new Date(endTime.getTime() + 60 * 60 * 1000);
        List<String> slotList = new ArrayList<>();
        Calendar instance = Calendar.getInstance();
        instance.setTime(startTime);
        while (instance.getTimeInMillis() < realEndTime.getTime()) {
            slotList.add(format(instance.getTime(), format));
            instance.add(Calendar.HOUR_OF_DAY, offset);
        }
        return slotList.stream().toArray(String[]::new);
    }

    /**
     * 在时间范围内 按天分段
     *
     * @param startTime 开始时间 2022-01-01
     * @param endTime   结束时间 2022-01-02
     * @param format    输出格式 yyyy-MM-dd
     * @param offset    偏移量 1
     * @return 2022-01-01 2022-01-02
     */
    public static String[] timeSlotOfDay(Date startTime, Date endTime, FormatType format, int offset) {

        //去除小时的影响
        endTime = DateUtil.beginOfDay(endTime);

        //多一天
        Date realEndTime = new Date(endTime.getTime() + 24L * 60 * 60 * 1000);

        List<String> slotList = new ArrayList<>();
        Calendar instance = Calendar.getInstance();
        instance.setTime(startTime);
        while (instance.getTimeInMillis() < realEndTime.getTime()) {
            slotList.add(format(instance.getTime(), format));
            instance.add(Calendar.DATE, offset);
        }
        return slotList.stream().toArray(String[]::new);
    }


    /**
     * 在时间范围内按月分段
     *
     * @param startTime 开始时间 2022-01
     * @param endTime   结束时间 2022-03
     * @param format    输出格式 yyyy-MM
     * @param offset    偏移量 1
     * @return 2022-01 2022-02 2022-03
     */
    public static String[] timeSlotOfMonth(Date startTime, Date endTime, FormatType format, int offset) {

        //去除几日的影响
        endTime = DateUtil.beginOfMonth(endTime);

        //多一个月
        Calendar endCalendar = Calendar.getInstance();
        endCalendar.setTime(endTime);
        endCalendar.add(Calendar.MONTH, 1);

        List<String> slotList = new ArrayList<>();
        Calendar instance = Calendar.getInstance();
        instance.setTime(startTime);
        while (instance.getTimeInMillis() < endCalendar.getTime().getTime()) {
            slotList.add(format(instance.getTime(), format));
            instance.add(Calendar.MONTH, offset);
        }
        return slotList.stream().toArray(String[]::new);
    }

    /**
     * 将一段时间按天拆分,Tuple1是开始时间,Tuple2是结束时间
     * @param startTime 开始时间字符串
     * @param endTime 结束时间字符串
     * @return 按天拆分的map 例:{"2023-01-01":{"t1":"2023-01-01 08:00:00","t2":"2023-01-01 12:00:00"}}
     */
    public static Map<String, Tuple2<Date,Date>> spliceDate(Date startTime, Date endTime){
        Map<String,Tuple2<Date,Date>> map = new HashMap<>();
        assert startTime != null;
        assert endTime != null;

        Date start = startTime;
        //按时间段拆分
        while(true){
            String dayStr = DateUtils.format(start, DateUtils.FormatType.SIMPLE);
            DateTime endOfDay = DateUtil.endOfDay(start);
            if(endOfDay.getTime() < endTime.getTime()){
                //说明今天没有结束
                map.put(dayStr, Tuples.of(start,DateUtil.endOfDay(start)));
                start = DateUtil.beginOfDay(DateUtil.offsetDay(start,1));
            }else{
                //结束
                map.put(dayStr,Tuples.of(start,endTime));
                break;
            }
        }
        return map;

    }

    public static void main(String[] args) throws ParseException {
        String[] strings = timeSlotOfHour(DateUtil.beginOfMonth(new Date()), DateUtil.endOfMonth(new Date()), FormatType.DAY, 1);
        System.out.println(JacksonUtils.toJsonString(strings));
    }
}
